/**
 * 给定一个树，每一次给定[s,e]区间以及一个点z查询 
 * SIGMA{Depth[LCA(i,z)], i属于[s,e]}
 * 显然不能一个一个点的查。首先考虑给定区间[1, e]，在不求出具体的LCA的情况下如何查询。
 * 1为树根
 * 只需要for i in [1, e]:
 *     将1到i的路径上的每个点权加1
 * 在查询1到z的路径上的权值和即可
 * 因为具备可加可减，因此就能求出[s,e]区间内的答案
 * for i in [1, e]:
 *    路径加1
 *    if i == s - 1: 减去1到i的路径和
 *    if i == e: 加上1到i的路径和
 * 最后考虑多个循环的情况，路径加1的操作显然只能做一遍
 * 因此要离线。将所有操作的左右端点记住。
 * for i in [1, N]:
 *     if i 是第j个询问的左端点减一, ans[j] -= 1到i的路径和
 *     if i 是第j个询问的右端点， ans[j] += 1到i的路径和
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>

using llt = long long;
using pii = pair<int, int>;
using vpii = vector<pii>;
using vi = vector<int>;
using vll = vector<llt>;

template<typename T>
void input(vector<T>&v, int n){
    v.assign(n + 1, {});
    for(int i=1;i<=n;++i) cin >> v[i];
}

template<typename T>
istream & operator >> (istream & is, vector<T> & v){
    for(auto & i : v) is >> i;
    return is;
}

llt const INF = 0x1F2F3F4F5F6F7F8F;
llt const NINF = -INF;
llt const MOD = 201314;

struct HLD{ // 重链剖分

using llt = long long;

using value_type = int;  
vector<value_type> data; // 线段树

using lazy_type = int;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    // assert(0);
	return ls + rs;
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    // assert(0);
    data[t] += (e - s + 1) * delta;
    lazy[t] += delta;
	return; 
}

static value_type mkValue(llt data){
    // assert(0);
	return data;
}

/// 对于有顺序要求的值需要倒序操作
static void reverse(value_type & v){
    // assert(0);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = 0;
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = 0;
    return VALUE0;
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero();
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}

int N;
/// 树结构, 1-index
vector<vector<int>> g;
/// 点权值
vector<llt> weight;

/// 建单向边
void mkDiEdge(int a, int b){
    g[a].push_back(b);
}
/// 建双向边
void mkBiEdge(int a, int b){
    mkDiEdge(a, b); mkDiEdge(b, a);
}

/// 树链剖分结构
struct node_t{
    int parent; // 父节点
    int hson;   // 重儿子
    int depth;  // 该节点的深度, 根节点深度为0
    int size;   // 本节点所领子树的节点总数
    int top;    // 本节点所在重链的顶
    int nid;    // 本节点在线段树中的编号, 即dfs序
    int mdes;   // 本节点所领子树的线段树编号均在[nid, mdes]中
};

int root; // 树根
vector<int> nid2old; // nid2old[i]表示线段树中第i个节点在原树中的编号
int timestamp; // 辅助变量
vector<node_t> nodes;

/// 递归找重边
void _dfsHeavyEdge(int u, int p, int d){
    auto & n = nodes[u];
    n.parent = p;
    n.depth = d;
    n.size = 1;

    for(auto v : g[u]){
        if(v == p) continue;
        _dfsHeavyEdge(v, u, d + 1);
        n.size += nodes[v].size;
        if(nodes[n.hson].size < nodes[v].size) n.hson = v;
    }
    return;
}

/// 递归找重链
void _dfsHeavyPath(int u, int top){
    auto & n = nodes[u];
    n.top = top;
    nid2old[n.mdes = n.nid = ++timestamp] = u;

    if(0 == n.hson) return;
    _dfsHeavyPath(n.hson, top);
    n.mdes = max(n.mdes, nodes[n.hson].mdes);

    for(auto v : g[u]){
        if(v != n.parent and v != n.hson){
            _dfsHeavyPath(v, v);
            n.mdes = max(n.mdes, nodes[v].mdes);
        }
    }
    return;
}
/// 递归建线段树
void _build(int t, int s, int e) {
    if(s == e) {
        // data[t] = mkValue(weight[nid2old[s]]); // 注意线段树编号与原树编号存在转换
		data[t] = 0;
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 初始化, n是树的点数
void init(int n){
    N = n;
    timestamp = 0;
    /// 初始化树结构
    g.assign(N + 1, {});
    weight.assign(N + 1, 0);
    /// 初始化树链结构
    nodes.assign(N + 1, {0, 0, 0, 0, 0, 0, 0});
    nid2old.assign(N + 1, 0);
    /// 初始化线段树结构
    data.assign(N + 1 << 2, value_zero());
    lazy.assign(N + 1 << 2, lazy_zero());    
}

/// 在输入所有数据以后构建
void build(int root){
    /// 建树链
    _dfsHeavyEdge(this->root = root, 0, 0);
    _dfsHeavyPath(root, root);
    /// 建线段树
    _build(1, 1, N);
}

/// 求原树上x和y的LCA
int lca(int x, int y){
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) y = nodes[nodes[y].top].parent;
        else x = nodes[nodes[x].top].parent;
    }
    return nodes[x].depth <= nodes[y].depth ? x : y;
}

/// 查询原树上x到y的路径信息
value_type query(int x, int y){
    value_type ans = value_zero();
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) swap(x, y);

        ans = _up_(ans, _query(1, 1, N, nodes[nodes[x].top].nid, nodes[x].nid));
        x = nodes[nodes[x].top].parent;
    }
    if(nodes[x].depth > nodes[y].depth) swap(x, y);
    ans = _up_(ans, _query(1, 1, N, nodes[x].nid, nodes[y].nid));
    return ans;
}

/// 查询原树上x子树的信息
value_type query(int x){
    return _query(1, 1, N, nodes[x].nid, nodes[x].mdes);
}

/// 原树上x到y的路径修改
void modify(int x, int y, const lazy_type & delta){
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) swap(x, y);

        _modify(1, 1, N, nodes[nodes[x].top].nid, nodes[x].nid, delta);
        x = nodes[nodes[x].top].parent;
    }
    if(nodes[x].depth > nodes[y].depth) swap(x, y);
    _modify(1, 1, N, nodes[x].nid, nodes[y].nid, delta);
    return;
}

/// 原树上x子树的修改
void modify(int x, const lazy_type & delta){
    _modify(1, 1, N, nodes[x].nid, nodes[x].mdes, delta);
}

};

int Q;
int N;
HLD Tree;
vector<int> Ans;
map<int, vpii> Left, Right;

void work(){
    cin >> N >> Q;
    Tree.init(N);
    for(int a,i=2;i<=N;++i){
        cin >> a;
        Tree.mkDiEdge(a + 1, i);
    }
    Tree.build(1);
    
    Ans.assign(Q, 0);
    for(int a,b,z,i=0;i<Q;++i){
        cin >> a >> b >> z;
        assert(a <= b);
        if(a) Left[a].emplace_back(z + 1, i);
        Right[b + 1].emplace_back(z + 1, i);
    }

    for(int i=1;i<=N;++i){
        Tree.modify(1, i, 1);

        auto it = Left.find(i);
        if(it != Left.end()){
            for(const auto & p : it->second){
                auto tmp = Tree.query(1, p.first);
                (Ans[p.second] -= tmp) %= MOD;
            }
        }
        
        it = Right.find(i);
        if(it != Right.end()){
            for(const auto & p : it->second){
                auto tmp = Tree.query(1, p.first);
                (Ans[p.second] += tmp) %= MOD;
            }
        }

    }   

    for(auto i : Ans) cout << (i + MOD) % MOD << "\n";
    return;
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase; 
    while(nofkase--) work();    
    return 0;
}
